03. DDPG: 行动者
深度确定性策略梯度 (DDPG)
你可以使用很多种不同的算法来设计智能体,只要它适合连续状态和动作空间即可。一种热门方法是深度确定性策略梯度,简称 DDPG。它实际上是一种行动者-评论者方法,但是关键在于底层的策略函数本身确定性函数,从外部添加了一些噪点,以便采取的动作具有理想的随机性。
我们来实现原始论文中给出的算法:
Lillicrap, Timothy P等,2015. 深度强化学习连续控制. [pdf]
可以使用最现代的深度学习库(例如 Keras 或 TensorFlow)实现该算法的两大组件 - 行动者和评论者网络。
DDPG:行动者(策略)模型
以下是使用 Keras 定义的一个非常简单的行动者模型。
from keras import layers, models, optimizers
from keras import backend as K
class Actor:
"""Actor (Policy) Model."""
def __init__(self, state_size, action_size, action_low, action_high):
"""Initialize parameters and build model.
Params
======
state_size (int): Dimension of each state
action_size (int): Dimension of each action
action_low (array): Min value of each action dimension
action_high (array): Max value of each action dimension
"""
self.state_size = state_size
self.action_size = action_size
self.action_low = action_low
self.action_high = action_high
self.action_range = self.action_high - self.action_low
# Initialize any other variables here
self.build_model()
def build_model(self):
"""Build an actor (policy) network that maps states -> actions."""
# Define input layer (states)
states = layers.Input(shape=(self.state_size,), name='states')
# Add hidden layers
net = layers.Dense(units=32, activation='relu')(states)
net = layers.Dense(units=64, activation='relu')(net)
net = layers.Dense(units=32, activation='relu')(net)
# Try different layer sizes, activations, add batch normalization, regularizers, etc.
# Add final output layer with sigmoid activation
raw_actions = layers.Dense(units=self.action_size, activation='sigmoid',
name='raw_actions')(net)
# Scale [0, 1] output for each action dimension to proper range
actions = layers.Lambda(lambda x: (x * self.action_range) + self.action_low,
name='actions')(raw_actions)
# Create Keras model
self.model = models.Model(inputs=states, outputs=actions)
# Define loss function using action value (Q value) gradients
action_gradients = layers.Input(shape=(self.action_size,))
loss = K.mean(-action_gradients * actions)
# Incorporate any additional losses here (e.g. from regularizers)
# Define optimizer and training function
optimizer = optimizers.Adam()
updates_op = optimizer.get_updates(params=self.model.trainable_weights, loss=loss)
self.train_fn = K.function(
inputs=[self.model.input, action_gradients, K.learning_phase()],
outputs=[],
updates=updates_op)
注意,输出层生成的原始动作位于[0.0, 1.0]
范围内(使用 sigmoid 激活函数)。因此,我们添加另一个层级,该层级会针对每个动作维度将每个输出缩放到期望的范围。这样会针对任何给定状态向量生成确定性动作。稍后将向此动作添加噪点,以生成某个探索性行为。
另一个需要注意的是损失函数如何使用动作值(Q 值)梯度进行定义:
# Define loss function using action value (Q value) gradients
action_gradients = layers.Input(shape=(self.action_size,))
loss = K.mean(-action_gradients * actions)
这些梯度需要使用评论者模型计算,并在训练时提供梯度。因此指定为在训练函数中使用的“输入”的一部分:
self.train_fn = K.function(
inputs=[self.model.input, action_gradients, K.learning_phase()],
outputs=[],
updates=updates_op)